package com.atguigu.tingshu.order.service.impl;
import java.util.Date;

import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.order.LocalMsg;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.order.mapper.LocalMsgMapper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.google.common.collect.Maps;
import java.math.BigDecimal;

import java.util.*;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.MD5;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.adapter.PayWay;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.google.common.collect.Lists;

import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;


    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private LocalMsgMapper localMsgMapper;

    @Autowired
    //spting会把这个接口的实现类封装到这个集合bean对象中
    private List<PayWay> payWayList; //未来spring会默认给这个属性payWayList赋值PayWay接口的三个Bean对象


    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        //1.创造一个返回对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //2.判断订单的类型
        String itemType = tradeVo.getItemType();

        switch (itemType){
            case "1001": //专辑订单
                orderInfoVo  = delAlbumOrderType(tradeVo);
                break;
            case "1002": //声音订单
                orderInfoVo  = delTrackOrderType(tradeVo);
                break;
            case "1003": //Vip订单
                orderInfoVo  = delVipOrderType(tradeVo);
        }

        Map map = JSONObject.parseObject(JSONObject.toJSONString(orderInfoVo), Map.class);
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign); //生成订单结算页数据的订单签名(未来在提交提交订单的时候,要做订单的完整性校验用)

        //3.为了保证订单的多端重复提交的tradeNo;
        //拿到订单里商品的详细信息里的itemId集合
        List<Long> itemIds = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
        //把集合转为字符串并用MD5加密
        String str = itemIds.toString();
        String encrypt = MD5.encrypt(str);
        //拼接多端提交的订单号的key
        String multipyPortSubmitKey = "order:repeat:"+AuthContextHolder.getUserId()+":"+encrypt;
        //把这个key放入Redis中去,因为是根据key判断的value随便设置
        redisTemplate.opsForValue().set(multipyPortSubmitKey,"666");
        return orderInfoVo;
    }


    /**
     * 提交订单
     * @param orderInfoVo
     */
    @Override
    public Map<String,Object> submitOrder(OrderInfoVo orderInfoVo) {
        Long userId = AuthContextHolder.getUserId();
        String tradeNo = orderInfoVo.getTradeNo();

        //2.校验订单数据的完整性(对订单信息进行幂等性校验)
        //2.1校验是否非法请求
        if (StringUtils.isEmpty(tradeNo)){
            throw new GuiguException(201,"非法请求");
        }
        //2.2校验订单的完整性(md5加密)
        //我们没有办法直接把一个对象转换给另一个对象
        //将对象序列化为字符串(先将对象转换为字符串)
        String str = JSONObject.toJSONString(orderInfoVo);
        //把字符串反序列化为Map对象
        Map map = JSONObject.parseObject(str, Map.class);
        //md5加密,内容得一样 前端传过来的Vo对象中payWay字段是有值的,但是后端返回的Vo对象中PayWay字段是空的,所以需要手动设置一下前端的PayWay字段为空
        map.put("payWay","");
        SignHelper.checkSign(map);

        //2.3 校验订单是否重复提交 (订单做幂等性校验)[应该只生成一条信息]
        //(面试必问)一个接口如何让保证它的幂等性 1.redis+token 2.各种锁机制(本地锁 分布式锁) 3.数据库(数据库层面的锁 唯一索引+防重表...)
        //同一个请求 同一笔订单 发一次调用和多次调用 底层数据库对于的表都应该只保存这个订单的一条相关数据

        List<Long> itemIds = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
        String encrypt = MD5.encrypt(itemIds.toString());
        String key = "order:repeat:"+userId+":"+encrypt;
        //编写lue脚本原子性的判断并删除redis中的对于的key和值
        String script = "if redis.call(\"exists\",KEYS[1])\n" + "then\n" + "    return redis.call(\"del\",KEYS[1])\n" + "else\n" + "    return 0\n" + "end";
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key));
        if (execute==0){
            throw new GuiguException(ResultCodeEnum.REPEAT_SUBMIT);
        }

        //3.保存订单的数据(业务)
        /*
        订单的支付方式有三种:支付宝,微信,余额 ,需要判断使用的是哪一种订单方式
        解决方案:
        1. if else if else
        2. switch case
        3.设计模式:适配器模式(高级,优雅,帅!)
        适配器模式简化if else的编程开发
        适配器模式（Adapter Pattern）：将一个类的接口转换成客户端所期望的另一个接口，使得原本不兼容的类可以一起工作，提高代码的复用性。
         */

        //判读支付方式
        String payWay = orderInfoVo.getPayWay();

        //4.生成一个订单号
        String orderNo = RandomStringUtils.random(12, true, true);

        for (PayWay way : payWayList) {
            if (way.isSupport(payWay)){
                way.payWay(userId,orderNo,orderInfoVo);
            }
        }


        HashMap<String, Object> result = new HashMap<String, Object>();
        result.put("orderNo",orderNo);

        return result;
    }

    /**
     * 保存完整的订单信息
     * @param orderNo
     * @param userId
     * @param orderInfoVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderInfo(String orderNo, Long userId, OrderInfoVo orderInfoVo) {

        try {
            //添加了事务需要用代理对象调用 ,特别注意: 还需要去启动类中开启@EnableAspectJAutoProxy(proxyTargetClass = true)
            OrderInfoServiceImpl proxy = (OrderInfoServiceImpl) AopContext.currentProxy();
            //1.保存订单的基本信息
            Long orderId = proxy.saveOrderBasecInfo(orderNo,userId,orderInfoVo);
            //2.保存订单详情
            proxy.saveOrderDetail(orderId,orderNo,orderInfoVo);
            //3.保存订单的减免信息
            proxy.saveOrderDertail(orderId,orderNo,orderInfoVo);

            //4.向本地消息表插入一条数据
            LocalMsg localMsg = new LocalMsg();
            localMsg.setMsgContent(orderNo);
            localMsg.setStatus(0);
            localMsgMapper.insert(localMsg);


        } catch (Exception e) {
            throw new GuiguException(201,"服务内部出现异常");
        }
    }

    /**
     * 保存订单的减免信息
     * @param orderId
     * @param orderNo
     * @param orderInfoVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderDertail(Long orderId, String orderNo, OrderInfoVo orderInfoVo) {
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)){
            orderDerateVoList.stream().forEach(orderDerateVo -> {

                OrderDerate orderDerate = new OrderDerate();

                orderDerate.setOrderId(orderId);
                orderDerate.setDerateType(orderDerateVo.getDerateType()); //减免类型的码
                orderDerate.setDerateAmount(orderDerateVo.getDerateAmount());
                orderDerate.setRemarks(orderDerateVo.getRemarks());

                orderDerateMapper.insert(orderDerate);
            });
        }

    }


    /**
     * 保存订单详情
     * @param orderId
     * @param orderNo
     * @param orderInfoVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderDetail(Long orderId, String orderNo, OrderInfoVo orderInfoVo) {


        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)){
            orderDetailVoList.stream().forEach(orderDetailVo->{

                OrderDetail orderDetail = new OrderDetail();

                orderDetail.setOrderId(orderId);
                orderDetail.setItemId(orderDetailVo.getItemId());
                orderDetail.setItemName(orderDetailVo.getItemName());
                orderDetail.setItemUrl(orderDetailVo.getItemUrl());
                orderDetail.setItemPrice(orderDetailVo.getItemPrice());
                orderDetailMapper.insert(orderDetail);
            });
        }



    }


    /**
     * 保存订单的基本信息
     * @param orderNo
     * @param userId
     * @param orderInfoVo
     */
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderBasecInfo(String orderNo, Long userId, OrderInfoVo orderInfoVo) {
        OrderInfo orderInfo = new OrderInfo();

        orderInfo.setUserId(userId);
        String itemName = orderInfoVo.getOrderDetailVoList().get(0).getItemName();
        orderInfo.setOrderTitle(itemName);
        orderInfo.setOrderNo(orderNo);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfo.setOriginalAmount(orderInfoVo.getOriginalAmount());
        orderInfo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderInfo.setOrderAmount(orderInfoVo.getOrderAmount());
        orderInfo.setItemType(orderInfoVo.getItemType());
        orderInfo.setPayWay(orderInfoVo.getPayWay());

        orderInfoMapper.insert(orderInfo);

        return orderInfo.getId();
    }


    //vip订单
    private OrderInfoVo delVipOrderType(TradeVo tradeVo) {
        //0.从tradeVo中获取数据
        Long userId = AuthContextHolder.getUserId();
        Long itemId = tradeVo.getItemId(); //某一个套餐的id
        String itemType = tradeVo.getItemType();
        //1.构建一个返回对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //2.校验订单数据的生成(对订单信息进行幂等性校验)
        //2.1校验订单是否买过(这笔订单是否支付过) -->vip可以续费,允许重复购买,不用校验,但是需要修改时间
        //2.2校验订单是否在支付中(这笔订单下单了正准备支付)
       int count=orderInfoMapper.getUserPaddingVipTypeOrder(userId,itemId,itemType);
       if (count>0){
           throw new GuiguException(201,"该vip类型的订单未支付,请勿重复下单");
       }
        String tradeNo = RandomStringUtils.random(10, true, true);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(itemType);

        Result<VipServiceConfig>  vipServiceConfigResult= userInfoFeignClient.getVipServiceConfig(itemId);
        VipServiceConfig vipData = vipServiceConfigResult.getData();
        Assert.notNull(vipData,"远程调用用户微服务获取vip服务配置失败");
        BigDecimal price = vipData.getPrice().setScale(2, BigDecimal.ROUND_HALF_UP); //原价
        BigDecimal discountPrice = vipData.getDiscountPrice().setScale(2, BigDecimal.ROUND_HALF_UP); //折扣价
        orderInfoVo.setOriginalAmount(price);
        orderInfoVo.setDerateAmount(price.subtract(discountPrice));
        orderInfoVo.setOrderAmount(discountPrice);

        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(itemId);
        orderDetailVo.setItemName(vipData.getName());
        orderDetailVo.setItemUrl(vipData.getImageUrl());
        orderDetailVo.setItemPrice(price);
        orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVo));


        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType("1406");
        orderDerateVo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderDerateVo.setRemarks("VIP折扣详情");
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList(orderDerateVo));

        orderInfoVo.setTimestamp(System.currentTimeMillis());
        return orderInfoVo;
    }

    //声音订单
    private OrderInfoVo delTrackOrderType(TradeVo tradeVo) {
        //1.从tradeVo中获取数据
        Long userId = AuthContextHolder.getUserId();
        Long itemId = tradeVo.getItemId();
        String itemType = tradeVo.getItemType();
        Integer trackCount = tradeVo.getTrackCount();

        //2.校验订单数据的生成(对订单信息进行幂等性校验)
        //2.1校验订单是否买过(这笔订单是否支付过)
        //创建一个方法,既可以做判断,又可以做过滤
        Result<List<TrackInfo>> canPayTrack=albumInfoFeignClient.checkAndFilterTrackList(userId, itemId, trackCount);
        List<TrackInfo> canPaytrackInfos = canPayTrack.getData();
        Assert.notNull(canPaytrackInfos,"远程调用专辑微服务获取用户购买过的声音失败");
        //2.2校验订单是否在支付中(这笔订单下单了正准备支付)
        //拿下单未支付的声音id集合
        List<Long> trackIds= orderInfoMapper.getUserPaidTrackTypeOrder(userId,itemType);
        canPaytrackInfos.stream().forEach(trackInfo -> {
            if (trackIds.contains(trackInfo.getId())){
                throw new GuiguException(201,"购买的声音列表中存在已下单未支付的声音,请移除该声音,在下单购买...");
            }
        });
        //3.赋值
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        String tradeNo = RandomStringUtils.random(10, true, true);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(itemType);
        Result<TrackInfo> trackInfoResult= albumInfoFeignClient.getTrackInfoByTrackId(itemId);
        TrackInfo trackInfoResultData = trackInfoResult.getData();
        Assert.notNull(trackInfoResultData,"远程调用专辑微服务获取声音信息失败");
        Long albumId = trackInfoResultData.getAlbumId();
        Result<AlbumInfo> albumInfoAndTag = albumInfoFeignClient.getAlbumInfoAndTag(albumId);
        Assert.notNull(albumInfoAndTag,"远程调用专辑微服务获取专辑信息失败");
        AlbumInfo albumInfoAndTagData = albumInfoAndTag.getData();
        BigDecimal price = albumInfoAndTagData.getPrice().setScale(2, BigDecimal.ROUND_HALF_UP);//单集的价格(单集是不打折的)
        orderInfoVo.setOriginalAmount(price);
        orderInfoVo.setDerateAmount(new BigDecimal("0.00").setScale(2, BigDecimal.ROUND_HALF_UP));
        orderInfoVo.setOrderAmount(price.multiply(new BigDecimal(canPaytrackInfos.size())).setScale(2, BigDecimal.ROUND_HALF_UP));


        ArrayList<OrderDetailVo> orderDetailVos = new ArrayList<>();
        for (TrackInfo canPaytrackInfo : canPaytrackInfos) {
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(canPaytrackInfo.getId());
            orderDetailVo.setItemName(canPaytrackInfo.getTrackTitle());
            orderDetailVo.setItemUrl(canPaytrackInfo.getCoverUrl());
            orderDetailVo.setItemPrice(price);

            orderDetailVos.add(orderDetailVo);
        }
        orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVos));
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList()); //不打折,没有减免信息
        orderInfoVo.setTimestamp(System.currentTimeMillis());

        return orderInfoVo;
    }

    //专辑订单
    private OrderInfoVo delAlbumOrderType(TradeVo tradeVo) {

        Long userId = AuthContextHolder.getUserId();
        Long albumId = tradeVo.getItemId();
        Long itemId = tradeVo.getItemId();
        String itemType = tradeVo.getItemType();

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        //2.校验订单数据的生成(对订单信息进行幂等性校验)
        //2.1校验订单是否买过(这笔订单是否支付过)
        Result<Boolean> userPaidAlbum = userInfoFeignClient.getUserPaidAlbum(albumId, userId);
        Boolean paidAlbum = userPaidAlbum.getData();
        Assert.notNull(paidAlbum,"远程调用用户微服务获取用户购买过的专辑失败");
        if (paidAlbum){
            throw new GuiguException(201,"用户已经购买过该专辑");
        }
        //2.2校验订单是否在支付中(这笔订单下单了正准备支付)
        int count= orderInfoMapper.getUserPaidAlbumTypeOrder(userId,itemId,itemType);
        if (count>0){
            throw new GuiguException(201,"该笔专辑已经下单,且准备支付,请勿重复下单...");
        }

        //3.赋值
        //3.1简单属性的赋值
        String tradeNo = RandomStringUtils.random(10, true, true);
        orderInfoVo.setTradeNo(tradeNo); //订单交易号(追踪一个订单的编号) [三号:订单Id 订单编号 订单交易号]
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());

        //复杂属性的赋值
        Result<AlbumInfo> albumInfo = albumInfoFeignClient.getAlbumInfoAndTag(albumId);
        AlbumInfo data = albumInfo.getData();
        Assert.notNull(data,"远程调用专辑微服务获取专辑信息失败");
        BigDecimal originPrice = data.getPrice(); //原价
        BigDecimal discount = null;  //折扣
        Result<UserInfoVo> userInfoVo = userInfoFeignClient.getUserInfoByUserId(userId);
        UserInfoVo userInfoVoData = userInfoVo.getData();
        Assert.notNull(userInfoVoData,"远程调用用户微服务获取用户信息失败");
        Integer isVip = userInfoVoData.getIsVip();
        if ("1".equals(isVip+"")){
            discount = data.getVipDiscount();
        }else {
            discount= data.getVipDiscount(); //vip折扣
        }
        if (discount.intValue()==-1){
            discount = new BigDecimal(10.00);
        }
        // DerateAmount如何计算：原价：100 力度：7折 ：100（原价）-100*7/10=30
        BigDecimal derateAmount = originPrice.subtract(originPrice.multiply(discount).divide(new BigDecimal(10.00))).setScale(2, BigDecimal.ROUND_HALF_UP);

        orderInfoVo.setOriginalAmount(originPrice.setScale(2, BigDecimal.ROUND_HALF_UP));  //订单的原始金额(没打折)
        orderInfoVo.setDerateAmount(derateAmount);  // 优惠的多少钱
        orderInfoVo.setOrderAmount(originPrice.subtract(derateAmount).setScale(2, BigDecimal.ROUND_HALF_UP)); // 订单的实际金额（订单的原价-订单的优惠价格）


        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(albumId);
        orderDetailVo.setItemName(data.getAlbumTitle());
        orderDetailVo.setItemUrl(data.getCoverUrl());
        orderDetailVo.setItemPrice(originPrice);

        orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVo)); //订单的详情信息

        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType("1405");
        orderDerateVo.setDerateAmount(derateAmount);
        orderDerateVo.setRemarks("专辑折扣减免");
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList(orderDerateVo)); // 订单的减免信息
        orderInfoVo.setTimestamp(System.currentTimeMillis()); //生成订单结算页的时间戳(未来在提交订单的时候,要做订单的完整性校验)

        return orderInfoVo;
    }
}
